/*
 * The Clear BSD License
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2017 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted (subject to the limitations in the disclaimer below) provided
 *  that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "board.h"
#include "fsl_debug_console.h"
#include "fsl_gpio.h"
#include "fsl_flexspi.h"
#include "fsl_lpuart.h"

#include "pin_mux.h"
#include "clock_config.h"

#include "fsl_common.h"
#include "fsl_iomuxc.h"


#include "flex_spi_flash.h"
#include "bootloader.h"
#define USE_UART_OR_SDCARD 0

#if USE_UART_OR_SDCARD
#include "xmodem1k.h"
#include "ff.h"
extern const TCHAR driverNumberBuffer[3U];
UINT real_quantity = 0;
xmodem_fun_handle_t gp_xmodem_fun ={
    uartreadnoblock,
    uartsendnoblock,
    sysTimerClr,
    sysTimerGet,
    NULL,
    
};
#endif


extern status_t mount_sdcard(void);

uint8_t ProgramDataFromSDcard (uint8_t *ptr, uint32_t len);
uint8_t image_bufferRead[128];

/*******************************************************************************
 * Definitions
 ******************************************************************************/


/*******************************************************************************
 * Prototypes
 ******************************************************************************/


/*******************************************************************************
 * Variables
 ******************************************************************************/
volatile uint32_t boot_program_addr_point = BOOT_APP_START_ADDR_BASE;

volatile uint32_t g_systick_time_count = 0;

/*******************************************************************************
 * Code
 ******************************************************************************/

/**
 * \brief SYSTICK 中断处理函数
 * \return 无
 * \note SYSTICK中断处理属于内核中断，故无需调用
 *       am_int_connect() 和 am_int_enable() 函数。
 */
//void SysTick_Handler(void)
//{
//    g_systick_time_count++;
//    if ((g_systick_time_count % 500) == 0) {
//        GPIO_PortToggle(BOARD_USER_LED_GPIO, 1u << BOARD_USER_LED_GPIO_PIN);
//    }
//}


uint32_t sysTimerGet(void)
{
    return g_systick_time_count;
}

void sysTimerClr(uint32_t i)
{
    /* 设置当前的值为0 */
    g_systick_time_count = 0;
}


uint8_t uartsendnoblock (uint8_t *p_ch)
{
    if ((LPUART1->STAT & LPUART_STAT_TDRE_MASK)){
        LPUART1->DATA = (uint32_t)(*p_ch);
        return 1;
    } else {
        return 0;
    }
}

uint8_t uartreadnoblock (uint8_t *p_ch)
{
    uint32_t statusFlag;
#if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT
    uint32_t ctrl = LPUART1->CTRL;
    bool isSevenDataBits =
        ((ctrl & LPUART_CTRL_M7_MASK) || ((!(ctrl & LPUART_CTRL_M_MASK)) && (ctrl & LPUART_CTRL_PE_MASK)));
#endif

#if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO
    if (0 == ((LPUART1->WATER & LPUART_WATER_RXCOUNT_MASK) >> LPUART_WATER_RXCOUNT_SHIFT))
#else
    if (!(LPUART1->STAT & LPUART_STAT_RDRF_MASK))
#endif
    {
        statusFlag = LPUART_GetStatusFlags(LPUART1);

        if (statusFlag & kLPUART_RxOverrunFlag)
        {
            LPUART_ClearStatusFlags(LPUART1, kLPUART_RxOverrunFlag);
            return 0;
        }

        if (statusFlag & kLPUART_NoiseErrorFlag)
        {
            LPUART_ClearStatusFlags(LPUART1, kLPUART_NoiseErrorFlag);
            return 0;
        }

        if (statusFlag & kLPUART_FramingErrorFlag)
        {
            LPUART_ClearStatusFlags(LPUART1, kLPUART_FramingErrorFlag);
            return 0;
        }

        if (statusFlag & kLPUART_ParityErrorFlag)
        {
            LPUART_ClearStatusFlags(LPUART1, kLPUART_ParityErrorFlag);
            return 0;
        }
        
        return 0;
    }
#if defined(FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT) && FSL_FEATURE_LPUART_HAS_7BIT_DATA_SUPPORT
    if (isSevenDataBits)
    {
        *p_ch = (LPUART1->DATA & 0x7F);
    }
    else
    {
        *p_ch = LPUART1->DATA;
    }
#else
    *p_ch = (uint8_t)(LPUART1->DATA);
#endif

    return 1;   
}




uint8_t boot_data_program (uint8_t *ptr, uint32_t len)
{
    
    /*这是一个通用的接口，program的len不能随便乱改，必须得是FLASH_SECTOR_SIZE可以整除的数，同时在一次烧写的过程中，也必须用相同的len*/
    
    status_t status;
    int i = 0;
    
    PRINTF("????\r\n");
    if ((boot_program_addr_point % FLASH_SECTOR_SIZE) == 0) {
        /* Erase sectors. */
        status = flexspi_nor_flash_erase_sector(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE) );
        if (status != kStatus_Success)
        {
            return 0;
        }
    }
    PRINTF("???\r\n");
    if (len == 128) {
        status =
            flexspi_nor_flash_page_program(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, len);
        if (status != kStatus_Success)
        {
            return 0;
        }
        boot_program_addr_point += 128;
    } 
    
    else if(len == 64) {
        status =
            flexspi_nor_flash_page_program(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, len);
        if (status != kStatus_Success)
        {
            return 0;
        }
        boot_program_addr_point += 64;
    }
    
    else if(len == 32)  {
        status =
            flexspi_nor_flash_page_program(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, len);
        if (status != kStatus_Success)
        {
            return 0;
        }
        boot_program_addr_point += 32;
    }
    
    else if (len == 1024) {
        for (i = 0; i < (1024 / 256); i++) {
            status =
                flexspi_nor_flash_page_program(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, 256);
            if (status != kStatus_Success)
            {
                return 0;
            }
            boot_program_addr_point += 256;
            ptr += 256;
        }
    } else {
        return 0;
    }
    return 1;
}

uint8_t ProgramDataFromSDcard (uint8_t *ptr, uint32_t len)
{
    status_t status;
    int i = 0;
    
    if (((boot_program_addr_point) % FLASH_SECTOR_SIZE) == 0) {
				PRINTF("erase！now pointer is：%p\r\n",boot_program_addr_point);
        /* Erase sectors. */
        status = flexspi_nor_flash_erase_sector(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE));
        if (status != kStatus_Success)
        {
            return 0;
        }
    }
    
    if (len == 128) {
        status =
            flexspi_nor_flash_page_program(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, len);
        if (status != kStatus_Success)
        {
            return 0;
        }
        boot_program_addr_point += 128;
    } 
		else if(len<128)
		{
				status =
            flexspi_nor_flash_page_program(FLEXSPI, (boot_program_addr_point - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, len);
        if (status != kStatus_Success)
        {
            return 0;
        }
        boot_program_addr_point += len;
		}
    return 1;
}

uint8_t subsection_program(uint8_t *ptr,uint32_t len)
{
    uint8_t Program_Times = 0;
    uint8_t Program_tail = 0;
    Program_Times = len/128;
    Program_tail = len%128;
    
    uint16_t Dvi = 128;
        
    switch(len)
    {
        case 4096:
            for(uint8_t i =0;i<4096/Dvi;i++)
            {
                ProgramDataFromSDcard(&ptr[i*Dvi],Dvi);
            }
            break;
        case 2048:
            for(uint8_t i =0;i<2048/Dvi;i++)
            {
                ProgramDataFromSDcard (&ptr[i*Dvi],Dvi);
            }
            break;
        case 1024:
            for(uint8_t i =0;i<1024/Dvi;i++)
            {
                ProgramDataFromSDcard (&ptr[i*Dvi],Dvi);
            }
            break;
        case 512:
            for(uint8_t i =0;i<512/Dvi;i++)
            {
                ProgramDataFromSDcard (&ptr[i*Dvi],Dvi);
            }
            break;
        case 256:
            for(uint8_t i =0;i<256/Dvi;i++)
            {
                ProgramDataFromSDcard (&ptr[i*Dvi],Dvi);
            }
            break;
        case 128:
            ProgramDataFromSDcard (ptr,Dvi);
            break;     
        default:
            if(len>128)
            {
                for(uint8_t i =0;i<Program_Times;i++)
                {
                    ProgramDataFromSDcard (&ptr[i*Dvi],Dvi);
                }
                ProgramDataFromSDcard (&ptr[(Program_Times)*Dvi],Program_tail);
            }
            else if(len<128)
            {
                ProgramDataFromSDcard (ptr,len);
            }
        
            break;
    }
    return 1;
}

uint8_t ProgramDataWithAddr (uint8_t *ptr, uint32_t len, uint32_t program_addr)
{
    status_t status;
    int i = 0;
    __disable_irq();
    if (((program_addr) % FLASH_SECTOR_SIZE) == 0) {
				//PRINTF("erase！now pointer is：%p\r\n",program_addr);
        /* Erase sectors. */
        status = flexspi_nor_flash_erase_sector(FLEXSPI, (program_addr - EXAMPLE_FLEXSPI_AMBA_BASE));
        if (status != kStatus_Success)
        {
            return 0;
        }
    }
    
    if (len == 128) {
        status =
            flexspi_nor_flash_page_program(FLEXSPI, (program_addr - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, len);
        if (status != kStatus_Success)
        {
            return 0;
        }
       
    } 
		else if(len<128)
		{
				status =
            flexspi_nor_flash_page_program(FLEXSPI, (program_addr - EXAMPLE_FLEXSPI_AMBA_BASE), (void *)ptr, len);
        if (status != kStatus_Success)
        {
            return 0;
        }
        
		}
    return 1;
}

uint8_t subsection_program_with_addr(uint8_t *ptr,uint32_t len,uint32_t addr)
{
    uint8_t Program_Times = 0;
    uint8_t Program_tail = 0;
    Program_Times = len/128;
   
    Program_tail = len%128;
    uint16_t Dvi = 128;
    if(len>128)
    {
        for(uint8_t i =0;i<Program_Times;i++)
        {
            ProgramDataWithAddr (&ptr[i*Dvi],Dvi,addr);
            addr+=128;
        }
        if(Program_tail!=0)
        {
            ProgramDataWithAddr (&ptr[(Program_Times)*Dvi],Program_tail,addr);
        }
    }
    else if(len<128)
    {
        ProgramDataWithAddr (ptr,len,addr);
    }
    return 0;
}


/*********************************************************************************************************
** Function name:       vControlSwitch
** Descriptions:        控制转换到用户代码程序
** input parameters:    无
** output parameters:   无
** Returned value:      无
*********************************************************************************************************/

typedef void (*JUMP_APP_FUN_T) (void);
JUMP_APP_FUN_T Jump_To_Application;
void vControlSwitch (void)
{
  uint32_t JumpAddress;
  
  JumpAddress = *(uint32_t*) (BOOT_APP_START_ADDR_BASE + 4);
  Jump_To_Application = (JUMP_APP_FUN_T) JumpAddress;
  __set_MSP(*(uint32_t*) BOOT_APP_START_ADDR_BASE);
  Jump_To_Application();
}

/*********************************************************************************************************
** Function name:       vSceneRenew
** Descriptions:        具体控制器的Bootloader运行结束后初始化用户代码环境
**                      关闭SysTick，将向量表重定向到用户程序ROM的起始地址
** input parameters:    无
** output parameters:   无
** Returned value:      无
*********************************************************************************************************/
void vSceneRenew (void)
{
    SysTick->LOAD  = 0;                                                 /* reset the load register      */
    SysTick->VAL   = 0;                                                 /* reset the Counter Value      */
    SysTick->CTRL  = 0;                                                 /* disable SysTick Timer        */
    
    SCB->VTOR  = BOOT_APP_START_ADDR_BASE;                                   
}

void delay(void)
{
    volatile uint32_t i = 0;
    for (i = 0; i < 8000000; ++i)
    {
        __asm("NOP"); /* delay */
    }
}

volatile uint32_t g_systick_time_dest = 0;

void ms_delay (uint32_t ms)
{
    g_systick_time_dest = ms + g_systick_time_count;
    
    while (g_systick_time_count < g_systick_time_dest);
}
/*!
 * @brief Main function
 */

void jump_app(void)
{
		LPUART_Deinit(LPUART1);
    vSceneRenew();
    vControlSwitch();
		__disable_irq();
		__DSB();
		__ISB();
}
#if USE_UART_OR_SDCARD
int GetImageFromUart(void)
{
    status_t status;
    /* Board pin, clock, debug console init */
    /* Init output LED GPIO. */
		
    PRINTF("\r\nStart Get ApplicationImage From UART......... \r\n");

    boot_program_addr_point = BOOT_APP_START_ADDR_BASE;
    /* Check whether occur interupt and toggle LED */
    if (xmodem1k_client(boot_data_program,&gp_xmodem_fun,100,10000) == 0) {//boot_data_program
        PRINTF("\r\n Firmware Download Complete,Jump App. \r\n");
        PRINTF("------------------------------------------\r\n");
    } else{
        PRINTF("\r\n Wait timeout,Jump App. \r\n");
        if ((*(uint32_t *)(BOOT_APP_START_ADDR_BASE+4)) <= (BOOT_APP_START_ADDR_BASE+400) || (*(uint32_t *)BOOT_APP_START_ADDR_BASE+4) >= 0x60800000) {
            PRINTF("\r\n Error: Invalid applicaton. \r\n");
            while(1);
        }
        PRINTF("------------------------------------------\r\n");
    }
		jump_app();
		
		return 0;
}

int GetImageFromSDcard(void)
{
		FIL g_file;
		FILINFO file_info;
		DIR dir;
		FRESULT ret;
		status_t status;
		if(kStatus_Success==mount_sdcard())
		{
			PRINTF("\r\nStart Get ApplicationImage From SDCARD......... \r\n");
			ret = f_chdrive((char const *)&driverNumberBuffer[0U]);
			if (ret)
			{
					PRINTF("Change drive failed.\r\n");
					return -1;
			}
			ret=f_open(&g_file, _T("/boot.bin"),FA_READ);
			if(ret==FR_OK)
			{
					PRINTF("Find ApplicationImage Success and Open it\r\n");
			}
			else{
				PRINTF("Image error \r\n error code :%d\r\n",ret);
			}
			PRINTF("Start update ..........\r\n");
			memset(image_bufferRead,0,sizeof(image_bufferRead));
			boot_program_addr_point = BOOT_APP_START_ADDR_BASE;

			while((FR_OK==f_read(&g_file,image_bufferRead,(sizeof(image_bufferRead)),&real_quantity))&&(real_quantity!=0))
			{	
				PRINTF("%p read back size = %d\r\n",boot_program_addr_point,real_quantity);
        ProgramDataFromSDcard(image_bufferRead,real_quantity);
			}
			PRINTF("GOING TO APPLICATION\r\n");
			jump_app();
		}
		else{
				return 1;
			
		}
		return 0;
}
#endif




/*********************************************************************************************************
** Function name:       dectect_jump_app_or_startboot
** Descriptions:        检测并等待某标志量，该标志量决定是否进入app或进入boot，本函数
                        中选择等待GPIO3_IO04的低电平信号，若等待超时，则进入app
** input parameters:    两个函数指针，分别是开始等待计数，和结束等待计数。
** output parameters:   无
** Returned value:      无
*********************************************************************************************************/

extern bool GetOverTheloop;
void dectect_jump_app_or_startboot(void (*sand_glass)(void),void (*sand_glass_stop)(void),bool* DectetOverTime,void (*Start_Boot)(void))
{
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_04_GPIO3_IO04, 0U);   
    IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_04_GPIO3_IO04, 0xB0B0U);  
    gpio_pin_config_t sw_config = {
        kGPIO_DigitalInput,
        0,
        kGPIO_IntFallingEdge,
    };
    GPIO_PinInit(GPIO3, 4, &sw_config);
    sand_glass();
		*DectetOverTime = true;
    while(GPIO_PinRead(GPIO3,4)==1&&GetOverTheloop==false)
    {
      
    }
		if(GetOverTheloop==false)//证明GPIO是高电平
		{
				Start_Boot();
		}
		*DectetOverTime = false;
		GetOverTheloop = false;
    sand_glass_stop();   
}